package GameFiles;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Random;
import java.util.Scanner;
import java.util.Vector;

import EventManager.EventManager;
import EventManager.GameListener;
import EventManager.Listener;
import EventManager.StateMachineListener;
import GameStates.MainMenu;
import Gui.BottomStatsPanel;
import Gui.GameFrame;
import Main.Defines;
import Main.GameTimer;
import PlayerManager.PlayerManager;
import Cards.Card;
import Cards.AuctionCard;
import Cards.DealCard;
import Cards.EventCardGain;
import Cards.EventCardLoss;
import Cards.MailCardBill;
import Cards.MailCardMakeDeal;

public class GameManager
{
	private static GameManager ref;
	// SubSystem
	public GameTimer GameTimer;
	// Options Variables
	private int NumberOfPlayers;
	private int NumberOfMonths;
	private int NumberOfDays;
	private int DaysPerWeek;
	private int WeeksPerMonth;
	private int MAXMAIL;		// mailcards in a day
	private int StartingCash;
	// Main Game State Machine
	private StateBase CurrentGameState;
	private StateBase PreviousGameState;
	// Screen Variables
	private int ScreenWidth;
	private int ScreenHeight;
	// Game Dice
	private int Dice1;
	private int Dice2;
	private int DiceFaces;
	
	// lottery pile
	private int m_lotteryPile;
	public Random rand = new Random();

	private Vector<Card> DealCards = null;
	private Vector<Card> MailCards = null;
	private Vector<Card> EventCards = null;
	private Vector<Days> DayList = null;
	
	// Constructor
	private GameManager()
	{
		CurrentGameState = MainMenu.getInstance();
		PreviousGameState = MainMenu.getInstance();
		ScreenWidth = 800;
		ScreenHeight = 600;
		NumberOfPlayers = 2;
		NumberOfMonths = 1;
		NumberOfDays = 32;
		DaysPerWeek = 7;
		WeeksPerMonth = 5;
		MAXMAIL = 3; // mail cards in a day.
		m_lotteryPile = 0;

		DealCards = new Vector<Card>();
		MailCards = new Vector<Card>();
		EventCards = new Vector<Card>();
		DayList = new Vector<Days>();
		Dice1 = 0;
		Dice2 = 0;
		DiceFaces = 6;
	}
	// lottery stuff
	public int getLotteryPile(){
		return m_lotteryPile;
	}
	public void setLotteryPile(int value){
		m_lotteryPile = value;
	}
	public void addToLotteryPile(int value){
		m_lotteryPile += value;
	}
	public int getDaysPerWeek(){
		return DaysPerWeek;
	}
	
	public int getWeeksPerMonth(){
		return WeeksPerMonth;
	}

	/**
	 * Creates a GameManager Singleton
	 * 
	 * @return GameManger Object
	 */
	public static GameManager getInstance()
	{
		if (ref == null)
			// it's ok, we can call this constructor
			ref = new GameManager();
		return ref;
	}

	public void setNumberOfPlayers(int numberOfPlayers)
	{
		NumberOfPlayers = numberOfPlayers;
	}

	public int getNumberOfPlayers()
	{
		return NumberOfPlayers;
	}

	public void setNumberOfMonths(int numberOfMonths)
	{
		NumberOfMonths = numberOfMonths;
	}

	public int getNumberOfMonths()
	{
		return NumberOfMonths;
	}

	public void setStartingCash(int startingCash)
	{
		StartingCash = startingCash;
	}

	public int getStartingCash()
	{
		return StartingCash;
	}

	public void setScreenWidth(int screenWidth)
	{
		ScreenWidth = screenWidth;
	}

	public int getScreenWidth()
	{
		return ScreenWidth;
	}

	public void setScreenHeight(int screenHeight)
	{
		ScreenHeight = screenHeight;
	}

	public int getScreenHeight()
	{
		return ScreenHeight;
	}

	// TODO remove debug loader:
	public void PopulateDealCards()
	{
		Random rand = new Random();
		int whichcard = 0;
		for (int i = 0; i < 10; i++)
		{
			// Give a random Deal card to a random player.
			whichcard = rand.nextInt(DealCards.size());
			switch (DealCards.get(whichcard).getM_cardType())
			{
			case Card.DealCard:
				Defines.PLM.getPlayerList().get(
						rand.nextInt(Defines.PLM.getPlayerList().size()))
						.getDealCards().add(
								new DealCard(
										DealCards.get(whichcard).getName(),
										DealCards.get(whichcard).getValue(),
										((DealCard) DealCards.get(whichcard))
												.getCost()));
				break;
			case Card.DealCardAuction:
				break;
			default:
				break;
			}
		}
		// TODO Add DealCards List
	}

	// TODO remove debug loader:
	public void PopulateMailCards()
	{
		Random rand = new Random();
		int whichcard = 0;
		for (int i = 0; i < 10; i++)
		{
			// Give a random Deal card to a random player.
			whichcard = rand.nextInt(MailCards.size());
			switch (MailCards.get(whichcard).getM_cardType())
			{
			case Card.MailCardBill:
				Defines.PLM.getPlayerList().get(
						rand.nextInt(Defines.PLM.getPlayerList().size()))
						.getMailCards().add(
								new MailCardBill(MailCards.get(whichcard)
										.getName(), MailCards.get(whichcard)
										.getValue(), ((MailCardBill) MailCards
										.get(whichcard)).getCost()));
				break;
			case Card.MailCardBuyer:
				Defines.PLM.getPlayerList().get(
						rand.nextInt(Defines.PLM.getPlayerList().size()))
						.getMailCards().add(
								new MailCardMakeDeal(MailCards.get(whichcard)
										.getName(), MailCards.get(whichcard)
										.getValue()));

				break;
			default:
				break;
			}
		}
	}

	/*
	 * Read the deal cards from DealCards.txt DealCards.txt format:
	 * int,int,int,String, Cost,Value,Flag,Name,
	 * 
	 * Cost: The cost to purchase the deal Value: The payout when given to a
	 * buyer Flag: 0: normal deal; 1: Auction
	 */
	public void initializeDealCards() throws IOException
	{
		String pathstring = ".\\src\\InputFiles\\";
		Scanner filein = null;

		int flag; // The Card. enum of card types
		int cost;
		int value;
		String name;

		// TODO remove Debugging info
		File cd = null; // debug

		try
		{
			cd = new File("."); // debug
			//System.out.println("CD: " + cd.getCanonicalPath()); // debug

			filein = new Scanner(new BufferedReader(new FileReader(pathstring
					+ "DealCards.txt")));
			filein.useDelimiter(",\\s*");

			while (filein.hasNext())
			{

				flag = filein.nextInt();
				cost = filein.nextInt();
				value = filein.nextInt();
				name = filein.next();

				switch (flag)
				{
				case Card.DealCard:
					DealCards.add(new DealCard(name, value, cost));
					// System.out.println(filein.next());
					break;
				default:
					System.out.printf(
							"Invalid Deal Card %s in file DealCards.txt\n",
							name);
					break;
				}

			}
		} finally
		{
			if (filein != null)
			{
				filein.close();
			}
		}

	}

	// TODO remove this debug deal card printer
	public void printDealCards()
	{
		for (Card d : DealCards)
		{
			System.out.printf("%s  Cost: %d Value: %d\n", d.getName(),
					((DealCard) d).getCost(), d.getValue());
		}
	}

	// TODO remove this debug mail card printer
	public void printMailCards()
	{
		for (Card d : MailCards)
		{
			System.out.printf("Class: %s ", d.getClass().getSimpleName());
			System.out.printf("%s Value: %d\n", d.getName(), d.getValue());
		}
	}

	// TODO remove this debug mail card printer
	public void printEventCards()
	{
		for (Card d : EventCards)
		{
			System.out.printf("Class: %s ", d.getClass().getSimpleName());
			System.out.printf("%s Value: %d\n", d.getName(), d.getValue());
		}
	}
	
	// TODO remove this debug day printer
	public void printDays(){
		for ( Days d : DayList ){
			System.out.printf("%s\n", d.toString());
		}
	}

	// TODO remove this debug main.
	public static void main(String[] args) throws IOException
	{
		// test initialize.
		GameManager.getInstance().initializeDealCards();
		GameManager.getInstance().initializeMailCards();
		GameManager.getInstance().initializeEventCards();
		GameManager.getInstance().initializeDayList();
		// print lists.
		GameManager.getInstance().printDealCards();
		GameManager.getInstance().printMailCards();
		GameManager.getInstance().printEventCards();
		GameManager.getInstance().printDays();
	}

	public void initializeMailCards() throws IOException
	{
		String pathstring = ".\\src\\InputFiles\\";
		Scanner filein = null;

		// TODO remove Debugging info
		File cd = null; // debug

		try
		{
			//cd = new File("."); // debug
			//System.out.println("CD: " + cd.getCanonicalPath()); // debug

			filein = new Scanner(new BufferedReader(new FileReader(pathstring
					+ "MailCards.txt")));
			filein.useDelimiter(",\\s*");

			int mailcardtype;
			int cost;
			int value;
			String name;

			while (filein.hasNext())
			{

				mailcardtype = filein.nextInt();

				switch (mailcardtype)
				{
				case Card.MailCardBill:
					cost = filein.nextInt();
					value = filein.nextInt();
					name = filein.next();
					MailCards.add(new MailCardBill(name, value, cost));
					break;
				case Card.MailCardBuyer:
					cost = filein.nextInt();
					value = filein.nextInt();
					name = filein.next();
					MailCards.add(new MailCardMakeDeal(name, value));
					break;
				default:
					System.out.printf(
							"Invalid mail card type %d in MailCards.txt:\n",
							mailcardtype);
					System.out.printf("%s\n", filein.nextLine());
					break;
				}

			}
		} finally
		{
			if (filein != null)
			{
				filein.close();
			}
		}

	}

	// TODO Add EventCards List
	public void initializeEventCards() throws IOException
	{
		String pathstring = ".\\src\\InputFiles\\";
		Scanner filein = null;

		// TODO remove Debugging info
		File cd = null; // debug

		try
		{
			cd = new File("."); // debug
//			System.out.println("CD: " + cd.getCanonicalPath()); // debug

			filein = new Scanner(new BufferedReader(new FileReader(pathstring
					+ "EventCards.txt")));
			filein.useDelimiter(",\\s*");

			int cardtype;
			int cost;
			int value;
			String name;

			while (filein.hasNext())
			{

				cardtype = filein.nextInt();

				switch (cardtype)
				{
				case Card.EventCardGain:
					cost = filein.nextInt();
					value = filein.nextInt();
					name = filein.next();
					EventCards.add(new EventCardGain(name, value, cost));
					break;
				case Card.EventCardLoss:
					cost = filein.nextInt();
					value = filein.nextInt();
					name = filein.next();
					EventCards.add(new EventCardLoss(name, value, cost));
					break;
				default:
					System.out.printf(
							"Invalid event card type %d in MailCards.txt:\n",
							cardtype);
					System.out.printf("%s\n", filein.nextLine());
					break;
				}

			}
		} finally
		{
			if (filein != null)
			{
				filein.close();
			}
		}

	}

	public void initializeDayList() throws IOException {
		String pathstring = ".\\src\\InputFiles\\";
		Scanner filein = null;

		// TODO remove Debugging info
		File cd = null; // debug

		try
		{
			cd = new File("."); // debug
		//	System.out.println("CD: " + cd.getCanonicalPath()); // debug

			filein = new Scanner(new BufferedReader(new FileReader(pathstring
					+ "DayTypes.txt")));
			filein.useDelimiter(",\\s*");

			int daytype;
			int value;
			Random rand = new Random();

			while (filein.hasNext())
			{

				daytype = filein.nextInt();

				if ( daytype == Days.MAILDAY ){
					value = rand.nextInt(MAXMAIL)+1;
				}
				else {
					value = 1;
				}
				
				DayList.add(new Days(daytype, value));
				
			}
		} finally
		{
			if (filein != null)
			{
				filein.close();
			}
		}

	}

	// TODO Add InitializeSubSystems
	public boolean InitializeGame() throws IOException
	{
		GameTimer = new GameTimer();
		
		StateMachineListener SML = new StateMachineListener();
		GameListener GL = new GameListener();
		SML.init();
		GL.init();
		EventManager.getInstance().addListener(SML);
		EventManager.getInstance().addListener(GL);
		Defines.PLM.getPlayerList().clear();
		DealCards.clear();
		MailCards.clear();
		EventCards.clear();
		DayList.clear();
		Defines.PLM.initLotto();
		
		
		Defines.PLM.PopulatePlayerList();
		
		GameFrame.getInstance();
		BottomStatsPanel.getInstance().resetInstance();		
		MainMenu.getInstance().Enter();
		
		initializeDealCards();
		initializeMailCards();
		initializeEventCards();
		initializeDayList();
		return true;
	}

	public void ChangeGameState(StateBase newState)
	{
		PreviousGameState = CurrentGameState;
		CurrentGameState.Exit();
		CurrentGameState = newState;
		CurrentGameState.Enter();
		CurrentGameState.Draw(GameFrame.getInstance());
	}

	public void setCurrentGameState(StateBase currentGameState)
	{
		CurrentGameState = currentGameState;
	}

	public StateBase getCurrentGameState()
	{
		return CurrentGameState;
	}

	public void setPreviousGameState(StateBase previousGameState)
	{
		PreviousGameState = previousGameState;
	}

	public StateBase getPreviousGameState()
	{
		return PreviousGameState;
	}

	public void setDice1(int dice1)
	{
		Dice1 = dice1;
	}

	public int getDice1()
	{
		return Dice1;
	}

	public void setDice2(int dice2)
	{
		Dice2 = dice2;
	}

	public int getDice2()
	{
		return Dice2;
	}

	public void RollDice()
	{
		
		Dice1 = rand.nextInt(DiceFaces) + 1;
		Dice2 = rand.nextInt(DiceFaces) + 1;
	}

	public void setDayList(Vector<Days> dayList)
	{
		DayList = dayList;
	}

	public Vector<Days> getDayList()
	{
		return DayList;
	}

	public Vector<Card> getDealCards()
	{
		return DealCards;
	}

	public void setDealCards(Vector<Card> dealCards)
	{
		DealCards = dealCards;
	}

	public Vector<Card> getMailCards()
	{
		return MailCards;
	}

	public void setMailCards(Vector<Card> mailCards)
	{
		MailCards = mailCards;
	}

	public Vector<Card> getEventCards()
	{
		return EventCards;
	}

	public void setEventCards(Vector<Card> eventCards)
	{
		EventCards = eventCards;
	}

	public int getNumberOfDays() {
		return NumberOfDays;
	}

}
